package com.vecspace.middleplatform.filemanagment.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.alibaba.simpleimage.ImageWrapper;
import com.alibaba.simpleimage.SimpleImageException;
import com.alibaba.simpleimage.util.ImageReadHelper;
import com.vecspace.data.model.FilterColumn;
import com.vecspace.database.model.QueryResult;
import com.vecspace.exception.HException;
import com.vecspace.middleplatform.filemanagment.dao.UploadFileDao;
import com.vecspace.middleplatform.filemanagment.pojo.UploadFile;
import com.vecspace.middleplatform.filemanagment.pojo.UploadFileSimple;
import com.vecspace.middleplatform.filemanagment.pojo.UploadFileStorage;
import com.vecspace.security.model.LoginContext;
import com.vecspace.utils.Assert;
import com.vecspace.utils.UUIDUtil;
import com.vecspace.utils.log.HLogger;
import com.vecspace.web.HttpContentType;

import net.coobird.thumbnailator.Thumbnails;
/**
 * 
 * 功能描述 
 * @类型名称 UploadFileService
	* @author wanghua
 */
public class UploadFileService {

	@Autowired
	private UploadFileDao uploadFileDao;

	public UploadFile getUploadFile(String id) throws Exception{
		return uploadFileDao.getUploadFile(id);
	}

	public List<UploadFileSimple> getUploadFileListByIds(String[] ids) throws Exception{
		List<UploadFileSimple> uploadFiles = new ArrayList<>();
		if(ids != null && ids.length > 0) {
			for(String id : ids) {
				UploadFileSimple uploadFile = uploadFileDao.getUploadFileSimple(id);
				uploadFiles.add(uploadFile);
			}
		}
		return uploadFiles;
	}

	/**
	 * 根据多个查询条件执行分页查询 
	 * 
	 * @param queryFields	查询条件
	 * @param pageIndex	  页数 从1开始
	 * @param pageSize	   每页显示的数据条数,负数表示不分页
	 * @param sortField	  排序字段
	 * @param sortOrder	  排序方式 asc desc
	 * @return PageModel
	 * @throws Exception
	 * @reviewer
	 */
	public QueryResult<UploadFile> getUploadFileRecordExList(List<FilterColumn> queryFields,int page, int pageSize,String sortField, String sortOrder, String year) throws Exception{
		//设置默认分页
		page = page < 1 ? 1 : page;
		if(sortField == null || sortField.length() == 0){
			//默认排序字段
			sortField = "id";
		}
		return uploadFileDao.getUploadFileRecordExList(queryFields, page, pageSize, sortField, sortOrder, year);
	}

	/**根据id获取文件对象
	 * 
	 * @param id
	 * @return
	 * @throws Exception 
	 * @author  wanghua
	 * @reviewer  
	 */
	public File getFile(String id) throws Exception {
		UploadFile uploadFile = uploadFileDao.getUploadFile(id);
		File file = getFile(uploadFile);
		return file;
	}

	public UploadFile getUploadFileByAccessId(String accessId) throws Exception {
		UploadFile uploadFile = uploadFileDao.getUploadFileByAccessId(accessId);
		return uploadFile;
	}

	/**根据AccessId获取文件对象
	 * 
	 * @param id
	 * @return
	 * @throws Exception 
	 * @author  wanghua
	 * @reviewer  
	 */
	public File getFileByAccessId(String accessId) throws Exception {
		UploadFile uploadFile = uploadFileDao.getUploadFileByAccessId(accessId);
		File file = getFile(uploadFile);
		return file;
	}

	/**获取文件的完整路径（绝对路径），含文件名。
	 * 
	 * @param uploadFile
	 * @return 
	 * @author  wanghua
	 * @throws Exception 
	 * @reviewer  
	 */
	public File getFile(UploadFile uploadFile) throws Exception {
		String filePath = uploadFile.getFilePath();
		String storageId = uploadFile.getStorageId();
		UploadFileStorage fileStorage = fileStorageService.getUploadFileStorage(storageId);
		Assert.throwz(fileStorage != null, "存储路径不存在：storageId：" + storageId);
		String basePath = fileStorage.getStoragePath();
		File file = Paths.get(basePath, filePath).toFile();
		return file;
	}

	public File createOrGetThumbnailFile(UploadFile uploadFile, int level) throws Exception {
		String filePath = uploadFile.getFilePath(); //uuid + "_s1" + ".jpg";
		String thumbnailFilePath;
		int dotIdx = filePath.lastIndexOf('.');
		if(dotIdx > 0) {
			thumbnailFilePath = filePath.substring(0, dotIdx) + "_s"+level+".jpg";
		}else {
			thumbnailFilePath = filePath + "_s"+level+".jpg";
		}
		String storageId = uploadFile.getStorageId();
		UploadFileStorage fileStorage = fileStorageService.getUploadFileStorage(storageId);
		Assert.throwz(fileStorage != null, "存储路径不存在：storageId：" + storageId);
		String basePath = fileStorage.getStoragePath();
		File file = Paths.get(basePath, thumbnailFilePath).toFile();

		//如果没有找到缩略图,此时默认生成一份 add by chang
		if(!file.exists()){ 
			File baseFile = Paths.get(basePath, filePath).toFile(); 
			File thumbnail1File = new File(basePath, thumbnailFilePath); 
			try {
				if(level == 1) {
					this.imageScale(baseFile, thumbnail1File, 200, 0);
					uploadFile.setThumbnail1Width(200);
					uploadFile.setThumbnail1Height(0);
					if(thumbnail1File.exists()) {
						uploadFile.setThumbnail1Bytes((int)thumbnail1File.length());
					}
				}
				if(level == 2) {
					this.imageScale(baseFile, thumbnail1File, 150, 0);
					uploadFile.setThumbnail2Width(150);
					uploadFile.setThumbnail2Height(0);
					if(thumbnail1File.exists()) {
						uploadFile.setThumbnail2Bytes((int)thumbnail1File.length());
					}
				}
				saveUploadFile(uploadFile);
				return thumbnail1File;
			}catch(Exception ex){
				HLogger.DEFAULT().error(ex.getMessage());
			} 
		}
		return file;
	}
	
	public File createOrGetThumbnailFile(UploadFile uploadFile, int width, int height) throws Exception {
		String filePath = uploadFile.getFilePath(); //uuid + "_s1" + ".jpg";
		String thumbnailFilePath;
		String thumName = "_s"+width+"x"+height;
		int dotIdx = filePath.lastIndexOf('.');
		if(dotIdx > 0) {
			thumbnailFilePath = filePath.substring(0, dotIdx) + thumName +".jpg";
		}else {
			thumbnailFilePath = filePath + thumName +".jpg";
		}
		String storageId = uploadFile.getStorageId();
		UploadFileStorage fileStorage = fileStorageService.getUploadFileStorage(storageId);
		Assert.throwz(fileStorage != null, "存储路径不存在：storageId：" + storageId);
		String basePath = fileStorage.getStoragePath();
		String thumbnailBasePath = Paths.get(basePath, "thumbnail").toString();
		File path = new File(thumbnailBasePath);
		if(!path.exists()) {
			path.mkdirs();
		}
		
		File file = Paths.get(thumbnailBasePath, thumbnailFilePath).toFile();
		
		//如果没有找到缩略图,此时默认生成一份 add by chang
		if(!file.exists()){
			File thumbnailPath = file.getParentFile();
			if(!thumbnailPath.exists()) {
				thumbnailPath.mkdir();
			}
			File baseFile = Paths.get(basePath, filePath).toFile(); 
			File thumbnail1File = new File(thumbnailBasePath, thumbnailFilePath);
			try {
				this.imageScale(baseFile, thumbnail1File, width, height);
				file = thumbnail1File;
			}catch(Exception ex){
				HLogger.DEFAULT().error(ex.getMessage());
			} 
		}
		return file;
	}

	public String getImageContentType(UploadFile uploadFile) {
		String ext = uploadFile.getFileExt();
		if(ext == null)ext = "";
		ext = ext.toLowerCase();
		if(ext.equals(".jpg"))return HttpContentType.IMAGE_JPG;
		if(ext.equals(".png"))return HttpContentType.IMAGE_PNG;
		if(ext.equals(".gif"))return HttpContentType.IMAGE_GIF;
		if(ext.equals(".bmp"))return HttpContentType.IMAGE_BMP;
		if(ext.equals(".webp"))return HttpContentType.IMAGE_WEBP;
		return HttpContentType.DOWNLOAD;
	}

	public void saveUploadFile(UploadFile UploadFile) throws Exception{
		uploadFileDao.saveOrUpdateUploadFile(UploadFile);
	}

	public String buildSubPath(UploadFile uploadFile) {
		Date uploadTime = uploadFile.getUploadTime();
		Assert.throwz(uploadTime != null && uploadTime.getTime() > 100000, "uploadFile对象中没有设置上传时间");
		String subPath = this.getSubPath(uploadFile.getAppId(), uploadFile.getTenantId(), 
				uploadFile.getClassifyCode1(), uploadFile.getClassifyCode2(), uploadTime);
		return subPath;
	}

	/**获取存储目录，附件按日期分文件夹存储*/
	public String getSubPath(String appId, Long tenantId, String classifyCode1, String classifyCode2, Date date){
		DateFormat df = new SimpleDateFormat("yyyyMMdd");
		String datestr = df.format(date);
		String yearstr = datestr.substring(0,4);
		if(tenantId == null) { 
			tenantId = LoginContext.getTenantId();
		}
		if(StringUtils.isEmpty(appId)) appId = "a";
		if(StringUtils.isEmpty(classifyCode1)) classifyCode1 = "c1";
		if(StringUtils.isEmpty(classifyCode2)) classifyCode2 = "c2";

		String subPath = tenantId + File.separator + appId
				+ File.separator + classifyCode1
				+ File.separator + classifyCode2 
				+ File.separator + "F"+yearstr 
				+ File.separator + "F"+datestr;		
		return subPath;
	}

	/**从磁盘和数据库记录中删除附件
	 * 
	 * @param id
	 * @return
	 * @throws Exception 
	 * @author wanghua
	 * @reviewer: 
	 */
	public int deleteUploadFile(String id) throws Exception {
		UploadFile uploadFile = uploadFileDao.getUploadFile(id);
		Assert.throwz(uploadFile != null, "文件id不存在：" + id);

		File file = getFile(uploadFile);		
		if(file.exists()) {
			file.delete();
		}
		return uploadFileDao.deleteUploadFile(id);
	}

	/**
	 * 已上传文件文件重命名
	 * @param id
	 * @return
	 * @throws Exception 
	 * @reviewer: 
	 */
	public String updateUploadFileName(String id, String name) throws Exception {
		UploadFile uploadFile = uploadFileDao.getUploadFile(id);
		Assert.throwz(uploadFile != null, "文件id不存在：" + id);
		Assert.throwz(name.contains("."), "重命名时必须带有文件后缀！");
		uploadFile.setOrgFileName(name);
		uploadFileDao.saveOrUpdateUploadFile(uploadFile);
		return name;
	}

	/**上传一个文件。将http流中的文件写入磁盘，并记录文件信息到数据库中
	 * 
	 * @param request
	 * @param appId
	 * @param tenantId
	 * @param classifyCode1
	 * @param classifyCode2
	 * @param thumbnail1Width
	 * @param thumbnail1Height
	 * @param thumbnail2Width
	 * @param thumbnail2Height
	 * @param loginId
	 * @return
	 * @throws Exception 
	 * @author  wanghua
	 * @reviewer  
	 */
	public UploadFile uploadFile(HttpServletRequest request, 
			String appId, Long tenantId, String classifyCode1, String classifyCode2,
			int thumbnail1Width, int thumbnail1Height, 
			int thumbnail2Width, int thumbnail2Height, String loginId) throws Exception {
		List<UploadFile> uploadFiles = this.uploadFiles(request, appId, tenantId, classifyCode1, classifyCode2, 
				thumbnail1Width, thumbnail1Height, thumbnail2Width, thumbnail2Height, loginId);
		if(uploadFiles != null && uploadFiles.size() > 0)return uploadFiles.get(0);
		else return null;
	}
	/**上传一个或者多个文件。将http流中的文件写入磁盘，并记录文件信息到数据库中
	 * 
	 * @param request
	 * @param appId
	 * @param tenantId
	 * @param classifyCode1
	 * @param classifyCode2
	 * @param thumbnail1Width
	 * @param thumbnail1Height
	 * @param thumbnail2Width
	 * @param thumbnail2Height
	 * @param loginId
	 * @return
	 * @throws Exception 
	 * @author  wanghua
	 * @reviewer  
	 */
	public List<UploadFile> uploadFiles(HttpServletRequest request, 
			String appId, Long tenantId, String classifyCode1, String classifyCode2,
			int thumbnail1Width, int thumbnail1Height, 
			int thumbnail2Width, int thumbnail2Height, String loginId) throws Exception {

		MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
		MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
		if(multipartRequest.getFileMap() == null || multipartRequest.getFileMap().size() == 0) {
			throw new HException("没有上传文件");
		}

		List<UploadFile> uploadFiles = new ArrayList<>();
		Date uploadTime = new Date();
		Iterator<MultipartFile> multipartFileIterator = multipartRequest.getFileMap().values().iterator();
		while(multipartFileIterator.hasNext()) {
			MultipartFile multipartFile = multipartFileIterator.next();

			CommonsMultipartFile cFile = (CommonsMultipartFile) multipartFile;
			String orgFileName = cFile.getOriginalFilename();
			if(orgFileName == null || orgFileName.length()==0) throw new HException("没有获取到上传文件名");
			UploadFile uploadFile = this.saveUploadFile(dstFile ->{
				try {
					cFile.transferTo(dstFile);
				} catch (IllegalStateException | IOException ex) {
					HLogger.DEFAULT().error(ex.getMessage());
				}}, 
				orgFileName, appId, tenantId, classifyCode1, classifyCode2, 
				thumbnail1Width, thumbnail1Height, thumbnail2Width, thumbnail2Height, uploadTime, loginId);
			uploadFiles.add(uploadFile);
		}
		return uploadFiles;
	}

	public UploadFile uploadBase64Img(HttpServletRequest request, String base64Str, String fileName,
			String appId, Long tenantId, String classifyCode1, String classifyCode2,
			int thumbnail1Width, int thumbnail1Height, 
			int thumbnail2Width, int thumbnail2Height, String loginId) throws Exception {
		String[] baseStrAry = base64Str.split(",");
		String fileInfoStr = baseStrAry[0];
		String fileDetailStr = baseStrAry[1];
		if(fileInfoStr.indexOf("base64") < 0)
		{
			return null;
		}
		Date uploadTime = new Date();
		UploadFile uploadFile = this.saveUploadFile(dstFile ->{
			writeBase64Image(fileDetailStr, dstFile);
		}, fileName, appId, tenantId, classifyCode1, classifyCode2, 
				thumbnail1Width, thumbnail1Height, thumbnail2Width, thumbnail2Height, uploadTime, loginId);
		return uploadFile;
	}

	private UploadFile saveUploadFile(Consumer<File> writeToDstFile, String orgFileName, 
			String appId, Long tenantId, String classifyCode1, String classifyCode2,
			int thumbnail1Width, int thumbnail1Height, 
			int thumbnail2Width, int thumbnail2Height, Date uploadTime, String loginId) throws Exception {
		String uuid = UUIDUtil.createUUID();
		String newFileName = uuid;
		String extName = "";//包含 . 号
		int dotIndex = orgFileName.lastIndexOf('.');
		if(dotIndex >0){
			extName = orgFileName.substring(dotIndex);
		}
		newFileName = uuid + extName;
		String thumbnail1FileName = uuid + "_s1" + ".jpg";
		String thumbnail2FileName = uuid + "_s2" + ".jpg";

		//获取配置的存储路径（如果有多个可用存储路径，随机返回其中一个）
		UploadFileStorage uploadFileStorage = fileStorageService.getUploadFileStorage();
		Assert.throwz(uploadFileStorage != null, "没有配置存储路径");		
		String dstBasePath = uploadFileStorage.getStoragePath();			
		if(dstBasePath.endsWith("/") || dstBasePath.endsWith("\\")){
			dstBasePath = dstBasePath.substring(0, dstBasePath.length()-1);
		}

		String subPath = this.getSubPath(appId, tenantId, classifyCode1, classifyCode2, uploadTime);
		File path = Paths.get(dstBasePath, subPath).toFile();
		if(!path.exists()) {
			path.mkdirs();
		}

		File dstFile = new File(path, newFileName);
		File thumbnail1File = new File(path, thumbnail1FileName);
		File thumbnail2File = new File(path, thumbnail2FileName);

		String newFileNameWithSubPath = subPath + File.separator + newFileName;
		//同时添加UploadFile
		//cFile.transferTo(dstFile);
		writeToDstFile.accept(dstFile);
		String extNameLow = extName.toLowerCase();
		if(extNameLow.equals(".jpg") ||extNameLow.equals(".png") || extNameLow.equals(".gif")
				 || extNameLow.equals(".bmp")  || extNameLow.equals(".tif")  || extNameLow.equals(".webp")) {
			try {
				this.imageScale(dstFile, thumbnail1File, thumbnail1Width, thumbnail1Height);
				this.imageScale(dstFile, thumbnail2File, thumbnail2Width, thumbnail2Height);
			}catch(Exception ex){
				HLogger.DEFAULT().error(ex.getMessage());
			}
		}
		UploadFile uploadFile = new UploadFile();
		uploadFile.setStorageId(uploadFileStorage.getId());
		uploadFile.setOrgFileName(orgFileName);
		uploadFile.setFilePath(newFileNameWithSubPath);
		uploadFile.setAppId(appId);
		uploadFile.setTenantId(tenantId);
		uploadFile.setClassifyCode1(classifyCode1);
		uploadFile.setClassifyCode2(classifyCode2);
		uploadFile.setUploadTime(uploadTime);
		uploadFile.setUploader(loginId);
		uploadFile.setFileExt(extName);
		uploadFile.setFileBytes(dstFile.length());
		uploadFile.setAccessTimes(0L);
		uploadFile.setThumbnail1Width(thumbnail1Width);
		uploadFile.setThumbnail1Height(thumbnail1Height);
		uploadFile.setThumbnail2Width(thumbnail2Width);
		uploadFile.setThumbnail2Height(thumbnail2Height);
		if(thumbnail1File.exists()) {
			uploadFile.setThumbnail1Bytes((int)thumbnail1File.length());
		}
		if(thumbnail2File.exists()) {
			uploadFile.setThumbnail2Bytes((int)thumbnail2File.length());
		}
		uploadFileDao.saveOrUpdateUploadFile(uploadFile);
		return uploadFile;
	}
	/**生成缩略图，缩略图格式统一为JPG
	 * 如果 thumbnailWidth 和 thumbnailHeight都为0，则不生成缩略图
	 * @param srcImg
	 * @param thumbnailFile
	 * @param thumbnailWidth
	 * @param thumbnailHeight
	 * @throws IOException 
	 * @author  wanghua
	 * @throws SimpleImageException 
	 * @reviewer  
	 */
	private void imageScale(File srcImg, File thumbnailFile, int thumbnailWidth, int thumbnailHeight) throws IOException, SimpleImageException {
		if(thumbnailWidth <=0 && thumbnailHeight <= 0)return;
		Assert.throwz(thumbnailFile != null, "没有设置缩略图文件");
		try(FileInputStream inStream = new FileInputStream(srcImg)){
			ImageWrapper imageWrapper = ImageReadHelper.read(inStream);//阿里的ImageReadHelper,解决某些图片底色变色非常严重的问题

			int imgWidth = imageWrapper.getWidth();
			int imgHeight = imageWrapper.getHeight();

			double wscale = 0;
			double hscale = 0;
			if(thumbnailWidth > 0) {
				wscale = 1.0 * thumbnailWidth / imgWidth;
			}
			if(thumbnailHeight > 0) {
				hscale = 1.0 * thumbnailHeight / imgHeight;
			}
			double scale = Math.max(wscale, hscale);
			if(scale >= 1) {
				FileUtils.copyFile(srcImg, thumbnailFile);
			}else {
				Thumbnails.of(imageWrapper.getAsBufferedImage())
					.scale(scale) 
					.outputQuality(0.5)
					.outputFormat("JPEG")
					.toFile(thumbnailFile);
			}
		}
	}

	private boolean writeBase64Image(String imgBasr64Str, File dstFile) {
		if (imgBasr64Str == null)
			return false;
		try {
			// 解密
			Base64 base64 = new Base64(); //org.apache.commons.codec.binary.Base64;
			byte[] b = base64.decode(imgBasr64Str);			
			// 处理数据
			for (int i = 0; i < b.length; ++i) {
				if (b[i] < 0) {
					b[i] += 256;
				}
			}
			try(OutputStream out = new FileOutputStream(dstFile)){
				out.write(b);
				out.flush();
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	@Autowired
	private UploadFileStorageService fileStorageService;
}
